perm filename MEMO61.PUB[HAL,HE] blob
sn#117121 filedate 1974-08-28 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00019 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00003 00002 .NEWSEC SOURCE LANGUAGE,COMPILER
C00010 00003 .NEWSS (CONTROL STRUCTURES,CONTROL STRUCTURES)
C00015 00004 .tsk:NEWSSS PARTIAL ORDERING OF SUBTASKS
C00021 00005 .onm: NEWSSS ON MONITORS
C00029 00006 .NEWSSS UNITS
C00035 00007 .NEWSSS PROCEDURES
C00041 00008 .NEWSS (DATA STRUCTURES,DATA STRUCTURES)
C00046 00009 .dat: NEWSSS ALGEBRAIC DATATYPES
C00055 00010 .NEWSSS ARITHMETIC
C00061 00011 .NEWSSS SOME EXAMPLES OF ARITHMETIC EXPRESSIONS
C00063 00012 .NEWSS MOTIONS
C00072 00013 .NEWSSS OPTIONS FOR MOVES
C00081 00014 .dep: NEWSSS DEPROACHES
C00089 00015 .NEWSSS COMPLEX MOVES
C00094 00016 .NEWSSS SEARCHES
C00100 00017 .NEWSSS DEVICE CONTROL
C00105 00018 .att: NEWSS ATTACHMENT
C00111 00019 .gph:NEWSS GRAPH STRUCTURES
C00119 ENDMK
C⊗;
.NEWSEC SOURCE LANGUAGE,COMPILER
The following is a description of the source language of HAL.
We will discuss the structure of the compiler, and then these areas of
the language: control structures, data structures, motion
specifications, and various compile time constructs.
.com: NEWSS THE HAL COMPILER
The HAL compiler is built of three parts: the parser, the expander,
and the trajectory calculator. These are depicted in
{NEWFIG The HAL compiler,FULL,com←}.
.NEWSSS PARSER
The PARSER reads source code from either the console or a
file. Its purpose is to form parse trees and do some simple
manipulations, such as assigning line numbers, causing listings to
be directed to the appropriate file (if desired), expanding text
macros, and keeping a primitive symbol table. If a syntax error is
discovered, it informs the supervisor, which will give the user
several options, including aborting the compilation, making local
modifications on the spot, or switching temporarily to a text
editor. In many cases, control will be returned to the parser, and
it will continue to create parse trees from the input.
.NEWSSS EXPANDER
The EXPANDER shares with the trajectory calculator the responsibility
for turning parser output into code interpretable by the runtime
system. Its main functions are to maintain a model of the
expected runtime state at each point in the program and to use this
model to resolve a number of compile-time decisions. The information
kept includes expected variable values,
object descriptions, relations
between objects, endpoint constraints on particular trajectories, and
much more. Simple uses of this information include providing
the trajectory calculator with essential data and resolving
conditional compilation requests. Beyond this, the expander
has principal responsibility for filling in the details required
to turn calls on various high and intermediate level primitives
into runnable manipulation programs. It therefore
contains a number of quite specialized routines with considerable
knowledge about the domain of mechanical assembly, as well as a number
of more general mechanisms for coordinating the specialists.
The expander supplies to the trajectory calculator a structure which is
very similar to the parse trees it
accepts as input. However, no choices are left; all values have been
explicitly specified.
.NEWSSS TRAJECTORY CALCULATOR
The TRAJECTORY CALCULATOR takes the expanded code and
computes the required trajectories for the arms. Tables of
interpretable code are generated for handling arithmetic and
assignment operations, condition monitoring, and graph-structure
building operations (the runtime system keeps track of physical
attachment of objects). For motions, detailed instructions are
emitted specifying how each joint of each arm is to behave, what
computations to make at run-time for the modification of these
trajectories to bring them into correspondence with the current state
of the world (for it happens often that objects are not exactly where
they were planned to be), and what conditions to monitor during the
motion.
The trajectory calculator also is used to provide information
to the expander. For instance, it can predict the runtime effects
of a given modification of a planned trajectory. This information is
useful to the expander in deciding how many "different" trajectories
must be planned for a given motion request.
There are several errors which the trajectory calculator can
detect. A request might take the arm outside its range, or force a
joint to exceed its velocity limits. It may discover that there is a
possibility of collision between the two arms, or between the arm and
some object on the table. In order to carry out these tests, it may
request assurance from the user that some object lies within a
certain region, or it may give the user a warning. The world model
is used for much of this calculation. At its discretion, the
trajectory calculator may make some critical motions very slow, so
that an impending collision will be detected before it happens.
The output of the trajectory calculator is stored in binary
files, for loading into the PDP11.
.NEWSS (CONTROL STRUCTURES,CONTROL STRUCTURES)
.NEWSSS TRADITIONAL ALGOL STRUCTURES
HAL has many of the traditional ALGOL control structures,
including the statement, the block, the IF-THEN-ELSE conditional,
the WHILE loop, the FOR loop, and the GOTO (which in HAL is written
JUMP. JUMPs will not be implemented at first, because they confuse
the flow analysis needed for maintaining planning values and because
it is possible to accomplish much without them). The simple
statement can be of several varieties: Assignment, declaration,
manipulation, assertions, condition monitoring. The
assignment statement is of the general form
.UNFILL
FROB α← A + (2,0,0) ,
.REFILL
that is, a variable name, a left arrow, and a suitable expression.
The types of expressions available will be discussed under the rubric
of data types. Declarations are allowed anywhere in the code that
other statements are allowed; this facilitates typing in a program
from a terminal. Manipulation, the fundamental purpose of HAL, will
be discussed fully in the section on motion specifications.
Assertions are discussed in {sssref asr}.
Condition monitoring is discussed in {sssref onm}.
A block is a list of statements separated by semicolons,
prefaced by the reserved word BEGIN and closed by the reserved word
END. Blocks are used to enclose a group of statements to form what
syntactically can act as one statement, and provide a means for
keeping variables local to a piece of code. It is possible to name a
block by inserting its name as a string after the BEGIN; this is
useful as a comment, and during debugging provides a way to name
blocks of code. When a block is so named, the name should be repeated
immediately after the END; this provides an easy way to insure proper
matching of BEGINs and ENDs. An example:
.UNFILL
BEGIN "SAMPLE"
SCALAR A, I;
A α← 2;
FOR I α← 1 STEP 1 UNTIL 10 DO A α← A*A;
WHILE A > 0 DO
BEGIN "LOOP"
A α← A - 1;
IF A < 5 THEN WRITE(A) ELSE WRITE(A-5)
END "LOOP";
WRITE("DONE")
END "SAMPLE"
.REFILL
.NEWSSS COBEGIN-COEND
In addition to these traditional structures, there are also
some special ones for specifying simultaneous independent execution.
The principal device for this is the COBEGIN-COEND pair, which
brackets statements whose execution is meant to begin independently.
Each of the statements within the COBEGIN block will eventually get
executed, with whatever overlapping of execution might occur during
runtime (for example, while one arm is moving, another statement can
be computing; several arms can also work at the same time). The termination
of the block occurs only when each of the statements in the scope of
the COBEGIN has terminated.
.tsk:NEWSSS PARTIAL ORDERING OF SUBTASKS
An assembly task is often divided into subtasks which form a
partial order with respect to the order of execution. An example is
a task A which contains four subtasks, B, C, D, and E, of which B and
C must be done before D, and D must be done before E, but B and C
could be done in any order. It is possible in HAL to leave the
ordering of the subtasks up to the compiler, which will try to
optimize the entire operation. The way to specify the example just
given is as follows:
.UNFILL
TASK BEGIN "A"
BEGIN "B"
<code for task B>
END "B";
BEGIN "C"
<code for task C>
END "C";
BEGIN "D AND E"
PREREQUISITE "B", "C";
<code for task D>
<code for task E>
END "D AND E"
END "A"
.REFILL
The words TASK BEGIN introduce a "task block", which contains
a set of statements. These statements may be blocks identified by
strings and may contain the declaration that certain other named
blocks within the same task are prerequisite for the inception of
this block.
The order in which the statements are performed is determined
only insofar as the prerequisite conditions demand. The compiler may
reorder them consistently with the preconditions, and may even execute
some of the statements simultaneously (as if there were a COBEGIN),
if this is feasible.
It is useful to place assertions at the beginning of the code
for any of the statements within a task; this assists the compiler in
maintaining its world model, and is also used to help decide the
proper ordering of the tasks. It is likewise good to place assertions
at the end of each statement. (Assertions are discussed in {sssref asr}.)
.NEWSSS EVENTS: SIGNAL AND WAIT
To achieve simultaneous coordinated motion, one uses a
special form of the move commands which will be discussed later.
However, some simple synchronization is possible within the context of
simultaneous execution. This is achieved by means of explicit
events, which can be signaled and awaited. For every type of event
that the user wishes to use, there should be a declaration something
like:
.UNFILL
EVENT E1, E2, E3 .
.REFILL
Each event initially has count 0, that is, no signals have appeared
for it, and no process is waiting for it. The statement
.UNFILL
SIGNAL E1
.REFILL
increments the count associated with event E1, and if the resulting
count is 0 or negative, one of those processes waiting for E1 is
freed from its wait and readied for execution. The statement
.UNFILL
WAIT E1
.REFILL
decrements the count associated with event E1, and if the resulting
count is negative, the process issuing the WAIT is blocked from
continuing until a signal comes along. If the count is 0 or
positive, there is no waiting.
An example of the utility of this construct is inside a
COBEGIN block, where one path of execution requires that the other
path has passed some milestone. Here is how such a use might appear:
.UNFILL
EVENT MILESTONE;
COBEGIN
BEGIN "PATH 1"
<code before the critical point>
WAIT MILESTONE;
<code after the critical point>
END "PATH 1";
BEGIN "PATH 2"
<code in preparation for the milestone>
SIGNAL MILESTONE;
<code following the milestone>
END "PATH 2"
COEND
.REFILL
.onm: NEWSSS ON MONITORS
It is often desired to monitor some set of conditions
throughout a section of code. A special kind of statement which allows
the user to do this is the ON statement. A simple example is
.UNFILL
ON T >5 DO STOP YELLOW
.REFILL
which while active will monitor the magnitude of T
and if it should become greater than 5 will cause the yellow arm to
stop, if it is moving. An ON monitor has two states: enabled and
disabled. Generally, an ON monitor will be enabled as soon as its
defining statement is executed, and it becomes disabled when its
block is exited. Also, as soon as an ON monitor triggers, it becomes
disabled until explicitly reenabled. Reenabling is done by executing
the statement ENABLE within the conclusion (that is, the statement
following the DO). It is possible to name ON monitors; a monitor
named "CH" will be enabled when a statement of the form ENABLE "CH"
is executed, and is disabled either under the conditions named above
or when it is explicitly turned off by a DISABLE statement. To have
an ON monitor initially disabled, preface the ON with the word DEFER.
The condition which is being tested must be a simple numeric
inequality or equality, possibly using some continually evaluated
function. Boolean combinations are not allowed.
Some examples of ON monitors:
.UNFILL
"FUDGE" ON TEMPERATURE > 400 DO WRITE("BURNING");
DEFER "WAIT" ON COOKED = 1 DO DISABLE "FUDGE";
ON MARK > 3 DO ENABLE "WAIT";
.REFILL
It should be noted that this ability to enable and disable
monitors explicitly is a non-structured concept; using it will often
lead to unintelligible programs. In any case, scope rules must be
observed; it is not legal to enable or disable a monitor in a
parallel or subsidiary block. This means that two statements,
simultaneously executing inside a COBEGIN block, are not allowed to
interfere with each other's ON monitors.
The conclusion of an ON-monitor may be any statement,
including an entire block. The only restriction is that if a motion
statement is the only statement in the conclusion, it must be
surrounded by BEGIN and END. (This is necessary at times to prevent
ambiguity.) Here is a slightly more complex example:
.UNFILL
ON DURATION > 5 DO
BEGIN
"BIND" ON FORCE(Z)>10 DO STOP YELLOW;
ON DIST > 3 DO
BEGIN
DISABLE BIND;
VEL α← 40
END
END
.REFILL
The existence of ON monitors raises this question: When is a
block considered to be finished? It can happen that all the
executable statements have finished, but some ON monitors remain.
This situation is often sterile; nothing can happen unless one of the
conditions happens to trigger, which may or may not ever occur.
Therefore, a block is declared finished when no interpreters
(that is, pieces of straightforward code, exclusive of on-monitors)
remain
active within it. This means that if even one ON monitor has caused
an interpreter to start executing (to perform the statements in the
conclusion), then the block is not exited until that interpreter is
done. But a block may be exited while some ON monitors are still
enabled; this automatically disables them.
The user must be wary of the relative speeds at which her
various pieces of code in ON test conclusions get executed. When an
ON test triggers, any initial statements of enabling or disabling are
done immediately, but any arithmetic is scheduled to be done at some
point in the near future. Therefore it is not possible to guarantee
that a critical computation happen immediately. If the user desires,
she may use the word CRITICAL at the start of the conclusion, and
UNCRITICAL at the start of that code which need not be guaranteed
immediate execution.
Only one occurrence of CRITICAL, at the very start of the conclusion,
and only one occurrence of UNCRITICAL are allowed.
HAL automatically assumes CRITICAL before
statements of enabling and disabling, and UNCRITICAL immediately
following.
An example:
.UNFILL
ON T>0 DO
BEGIN "CONCLUSION"
ENABLE "GOOD GUY"; COMMENT: Assumed CRITICAL;
T α← 3; COMMENT: Assumed UNCRITICAL;
END "CONCLUSION";
ON S<0 DO
BEGIN "RESULT"
CRITICAL; COMMENT: Overrides defaults;
T α← 4; COMMENT: Will be done immediately;
UNCRITICAL; COMMENT: End of critical region;
DISABLE "GOOD GUY"; COMMENT: Done eventually;
END "RESULT"
.REFILL
.NEWSSS UNITS
Numbers are quite often used for measurements, and many
different systems of measurement exist. The UNITS statement serves
to inform the compiler which units are being used. The compiler uses its own
system for internal consistency, and does any scaling when necessary.
The default units, which the compiler itself uses, are placed first
in the following lists:
.UNFILL
Time: milliseconds, seconds, jiffies (that is, thirds: 60ths
of a second), minutes.
(The grain of the runtime is on the order of jiffies).
Distance: centimeters, inches.
Mass: grams, ounces, pounds, kilograms.
Angles: radians, degrees.
Rotations: Euler, Bolles, Taylor, Geomed. (These are explained
in {sssref dat}.)
An example:
UNITS SECONDS, DEGREES, TAYLOR;
.REFILL
.NEWSSS COMMENTS
As in Algol, comments are prefaced with the word COMMENT and
terminated by a semicolon. Also, any text enclosed in curly brackets,
as in
.UNFILL
α{We are the hollow menα}
.REFILL
will be ignored. The user can optionally reset the comment delimiters
from "α{α}" to whatever she wishes, by means of a require statement
such as:
.UNFILL
REQUIRE "α%α%" COMMENT_DELIMITERS .
.REFILL
which would cause the scanner to ignore any text between "α%" signs.
.NEWSSS LABELS
A LABEL is a point in the program to which a jump may be
made. Labels are not declared. An
example:
.UNFILL
FOO: A α← A + 1;
IF A < 100 THEN JUMP FOO;
.REFILL
Labels are in general useful mainly for debugging, and not for
program control, especially since JUMP will not be implemented at
first.
.NEWSSS ABORT
Occasionally the user wishes to stipulate that if the program
ever reaches a particular point, something is hopelessly wrong. The
statement ABORT causes the runtime to stop all moving devices and
to terminate execution. The supervisor is informed of the halt, and
will inform the user. ABORT takes an optional string argument, which
is a message which will be given to the user if the ABORT statement
is executed. An example:
.UNFILL
ABORT ("I KEEP MISSING THE BOLT!")
.REFILL
.NEWSSS OUTPUT
There are several ways that the user can request output
from HAL to the console. As mentioned above, ABORT can print
a message during execution. There is one other way to print a
message during execution, the WRITE statement, which
takes as arguments a list of variables and constants.
It is also legal to include a string constant in this list (there
are no string variables in HAL). Formatting of output is automatic.
An example:
.UNFILL
WRITE ("I think that the pump is at ",PUMP)
.REFILL
Some pieces of code are only intended to work under
certain conditions of planning knowledge. Such code might
have a check to insure that its preconditions are met; if not,
it is proper to signal a compile-time error, with a message.
This is done with the COMPILE_ERROR statement, which optionally
takes a string argument, and which will halt compilation and print
the message. One of the options the supervisor will give the user
is to proceed as if no error had been encountered. Here is
an example:
.UNFILL
COMPILE_ERROR("Hey! You didn't attach the pump to the hand!")
.REFILL
A similar statement which merely prints its message but
does not halt compilation is the COMPILE_WRITE statement, which
behaves in all respects like the runtime WRITE statement, in that
it can take variables and constants in its argument list, but where
variables are specified, the planning values will be printed. For
example:
.UNFILL
COMPILE_WRITE("YELLOW arm should be at",YELLOW)
.REFILL
.NEWSSS PROCEDURES
HAL has only a limited capacity for procedures. All
parameters to a procedure assume the planning value "undefined" at
the conclusion of a procedure call, except those which are declared
as VALUE parameters in the procedure heading, or those stated to be
UNCHANGED in the procedure call. There is no safeguard against the
accidental modification of "unchanged" parameters; to state
"unchanged" is entirely equivalent to an assertion that the parameter
has not changed its value during the execution of the procedure. The
declaration of a procedure is this:
.UNFILL
type PROCEDURE name (argument list) ,
.REFILL
where "type" is any datatype (and is optional), and "argument list"
is a list of parameter names with their types. An example:
.UNFILL
SCALAR PROCEDURE LGTH (FRAME F1, F2; VECTOR VALUE V1);
.REFILL
This declares that LGTH is a procedure which returns a scalar, and
takes as arguments two frames and one vector. The vector is not
changed by the procedure.
To call such a procedure:
.UNFILL
S1 α← LGTH(FROB, UNCHANGED HOLE, VECT);
.REFILL
This further asserts that HOLE is not changed by the call.
Assertions are essential at the start of a procedure body to
inform the compiler of the values to expect for the various
arguments. Remember that trajectories planned on the basis of highly
inaccurate planning values will not work well.
Assertions are discussed in {sssref asr}.
As a procedure is entered, all variables have planning value
"undefined". Globals may be accessed, but they also have undefined
initial planning value. All variables which have explicit or
implicit assignments within a procedure acquire the value
"undefined" at the point directly after the procedure call.
No modification of the attach structure is allowed inside a
procedure. The compiler (often wrongly) assumes that there are no
attachments involving variables within a procedure; to override this,
.UNFILL
ASSERT FACT (ATTACHED F1 F2) .
.REFILL
Attachments are discussed in {ssref att}.
There are four special types of procedure calls: A HAL
program might wish to call a routine coded for the PDP11 or a routine
coded for the PDP10. Likewise, a program on the PDP10 may wish to
control a HAL program, or a routine on the PDP11 may wish to request
some arm motion.
To achieve the first two cases, there exist "external
procedures" in HAL. These are compiled into calls on either routines
in the PDP11 or routines in the runtime PDP10 package. To declare
such a procedure:
.UNFILL
EXTERNAL PDP10 FRAME PROCEDURE FOO (FRAME A, B; VECTOR V) .
.REFILL
This declares the procedure FOO to be a procedure resident
in the runtime PDP10 package, expected to return a frame value, and
taking as arguments two frames and a vector. PDP10 procedures do not
have access to the actual variables sent, since copies are made;
therefore, all arguments to PDP10 procedures are considered to be
VALUE parameters.
It is possible to declare untyped (i.e., statement-like,
instead of expression-like) procedures as well. Replace "PDP10" with
"PDP11" for procedures in the PDP11. PDP11 procedures do have access
to values, and therefore parameters are not automatically considered
to be VALUE.
To achieve the second two cases, there exist "internal
procedures" in HAL. It is not necessary to state from where the
procedure may be called. Internal procedures must be at the top
level of a HAL program. A complete HAL program is considered to be
an untyped procedure without parameters.
.NEWSS (DATA STRUCTURES,DATA STRUCTURES)
.NEWSSS DECLARATIONS. PLANNING VALUES. ASSERTIONS
There are several available types of variables and constants,
as described below in detail. Each variable must be declared
somewhere before it is used; however, it is not necessary that this
declaration be at the top of a block. If a variable which has not
been declared is encountered, an attempt will be made to guess at its
type. (The compiler will, of course, give a warning.) This can lead
to unfortunate results, so it is best to declare all variables. The
compiler keeps track of a "planning value" for each variable. At
first, this value is "undefined"; any expression using an undefined
value itself has undefined value. Planning values become defined
through two mechanisms: Assertions and assignments. The statement
ASSERT X=3 will set the planning value
of X to 3.
(See {sssref asr} for details about assertions.)
The statement Xα←3 has the effect of setting the planning
value to 3 and causing code to be generated for the runtime to set
the value of X to three at this point in the program.
The purpose of
having planning values is severalfold, the most important use being
the calculation of proper trajectories. An example is
.UNFILL
ASSERT YELLOW=YPARK
.REFILL
which tells where the yellow arm is. Since at planning time
it is expected that some values will be known only roughly, provision
is made for the runtime to modify all trajectories before executing
them. This is the subject of a later discussion. The planning value
of any variable is accessible to the programmer: α#(A) is the planning
value of the expression A.
Assignments and assertions within loops pose a special problem for the
compiler. The question of when variables have good planning values
is a matter for research and debate; we have chosen this simple rule:
If a variable is neither assigned nor asserted in the loop, its
planning value stays the same as it was when the loop was begun.
Variables which have their value changed, either through assignment
or by assertion, have undefined planning value between the loop head
and the statement that changes them.
JUMPS would so complicate the planning-value computation that they
have not been implemented.
Here is an example of a loop, showing what planning values obtain
at various places:
.UNFILL
.begin
.narrow 8,8
A α← 1;
B α← 1;
C α← 1;
WHILE <condition> DO
BEGIN
α{α#(A)=1, α#(B)=UNDEFINED, α#(C)=UNDEFINEDα}
B α← 3;
α{α#(A)=1, α#(B)=3, α#(C)=UNDEFINEDα}
ASSERT C = α#(B);
B α← 4;
α{α#(A)=1, α#(B)=4, α#(C)=3α}
END;
α{α#(A)=1, α#(B)=UNDEFINED, α#(C)=UNDEFINEDα}
.end
.REFILL
.dat: NEWSSS ALGEBRAIC DATATYPES
The simplest datatype is the SCALAR, which is internally
represented as a fixed-point number. Scalars are used as time
intervals, space intervals, and as substructures upon which the more
complicated datatypes are built. The UNITS statement
allows the user to set the measurement system she would like to use,
so that a time scalar will be unambiguously interpreted as meaning
seconds, or jiffies, or whatever she wants. Some examples:
.UNFILL
SCALAR A, B;
A α← 2.4;
UNITS CENTIMETERS, SECONDS,RADIANS;
.COMT 12
COMMENT: These units
would only apply to A, for example, if A is used
as a distance, a time, or an angle. UNITS
statements do not obey Algol scope, so this one
will be in effect until the next one appears;
.END
B α← 3*A;
.REFILL
The next datatype is the VECTOR. A vector is written as a
triple of scalars separated by commas. An example is (2,x,4.3).
Vectors can refer either to location offsets (that is,
translations) or to orientation offsets (that is, rotations). The
context is sufficient to determine which of these meanings is
intended. When a vector refers to a translation, it is understood to
be in TABLE coordinates.
(TABLE is defined below.)
When a vector refers to a rotation, it can
either be represented in Euler angles (rotations about z, x', z''),
Bolles angles (rotations about x, y, z), Taylor angles (rotations
about z, x', y'), or Geomed angles (a, b, c, specifying the vector of
rotation whose magnitude is the angle of rotation).
Note that rotations are about the table coordinate system.
The UNITS
statement serves to set a default mode (as well as to determine
whether degrees or radians are being used).
There are three predefined vector constants: X, Y, and Z. These are
the unit vectors in TABLE coordinates.
.UNFILL
Examples:
VECTOR V1, V2;
V1 α← (3,1,A);
V2 α← V1 α∂ (π,0,0);
.REFILL
A FRAME is a location with an orientation. It has two basic
interpretations, which are closely related: 1) a hand position, and
2) the location and orientation of any object. TABLE is a predefined
frame. (It holds table coordinates.) Each arm (currently, YELLOW and
BLUE) is also a predefined frame; these frames are "read only" in the
sense that they may not appear to the left of an assignment arrow.
The values of YELLOW and BLUE are implicitly modified by arm motions,
however. The rest positions of the two arms are YPARK and BPARK,
which are frame constants.
Frames are described by a pair of vectors: one for position
and one for orientation. An example of a frame expression is
.UNFILL
[LOC : ORIENT]
.REFILL
where LOC is a vector specifying location, and ORIENT is a
vector for the orientation (with respect to the table).
Examples:
.UNFILL
FRAME F1, F2;
F1 α← [V1 : V2];
F2 α← F1 α∂ V2;
.REFILL
A PLANE is constructed from two vectors: the plane passes
through the first vector, and the outward-facing normal is in the
direction of the second vector. The syntax is
.UNFILL
VECTOR1 α\ VECTOR2.
.REFILL
Thus, the surface of the table is (0,0,0) α\ Z.
Planes divide the universe into three sets with respect to
the plane: inside, on, and outside. Outside are all points which
are on the side of the plane towards the outward-facing normal. To
determine where a point lies with respect to a plane, that is,
whether it is inside, on, or outside the plane, use the expression
.UNFILL
VECTOR %5.%* PLANE .
.REFILL
Its value is a scalar whose absolute value is the shortest
distance from the vector to the plane, and whose sign is negative if
the vector is inside the plane, 0 if the vector is on the plane, and
positive if the vector is outside the plane.
It is occasionally useful to construct a plane from four
scalars: the first three are the outward pointing normal vector
(which will be normalized if necessary); the fourth is the opposite
of the distance to the origin (of table coordinates). The way to
express such a plane is like this:
.UNFILL
(S1,S2,S3,S4) .
.REFILL
Examples:
.UNFILL
PLANE P1, P2;
P1 α← LOC(F1) α\ (X WRT F1); COMMENT: Y-Z plane of F1;
P2 α← P1 + (P1.(0,0,0))*NORMAL(P1); COMMENT: Moves P1 to origin;
S1 α← V1 . P1;
P1 α← (2,4,3.2,8.1); COMMENT: Fairly meaningless;
.REFILL
A TRANS is an operator acting on vectors, frames, planes, and
other transes. It is defined as the relation between two frames. An
example is FRAME1 α→ FRAME2. The value of this trans applied to
FRAME1 is FRAME2. That is, (FRAME1α→FRAME2)*FRAME1 is identically
equal to FRAME2. A trans can be built up from a rotation and a
translation; the expression is
.UNFILL
[TRANSLATION | ROTATION] .
.REFILL
Note that
rotation is applied first. Some people find it helpful to think of
frames as transformations: The transformation associated with a frame
A is
.UNFILL
TABLE α→ A .
.REFILL
When a frame is used in a context demanding a
transformation, it will be understood as a shorthand for the trans
leading from the table. Transes operate on the left. Examples:
.UNFILL
TRANS T1, T2;
T1 α← F1α→F2;
V1 α← T1*V2;
T2 α← T1*T1;
.REFILL
.NEWSSS ARITHMETIC
Here is a summary of the arithmetic expressions available.
They are grouped by the type of their values. These abbreviations
are used: `s' = scalar , `v' = vector , `f' = frame, `p' = plane, `t'
= trans.
%7
.BEGIN VERBATIM
.GROUP
scalar expressions:
s + s scalar addition (commutative)
s - s scalar subtraction
s * s scalar multiplication (commutative)
s / s scalar division
v . v dot product of two vectors (commutative)
| v | magnitude of vector
p . v signed distance from vector to plane (see discussion
above on planes)
.MAYBREAK
vector expressions:
(s,s,s) forming a vector from three scalars
s * v dilation of a vector
v + v vector addition (translation of the first vector by
the second) (commutative)
v ∂ v rotation of the first vector by the second
t * v transformation of a vector
f / f difference (rotation) between two frames
v WRT f a vector of length |v| rotated into f's system; like
v ∂ orient(f); that is, a vector in table
coordinates which looks to the table as v
does to f.
.MAYBREAK
frame expressions:
[v : v] forming a frame from location (first vector) and
an orientation (second vector)
f + v translating a frame; modifies only the location part
f ∂ v rotating a frame; modifies only the orientation part
t * f transformation of a frame
.MAYBREAK
plane expressions:
v \ v formation of a plane. Goes through first vector,
outward pointing normal is in direction of
the second vector.
(s,s,s,s) formation of a plane. First 3 scalars form outward-
pointing normal; last scalar is opposite of
distance to (table) origin.
p + v translation of a plane by a vector
p ∂ v rotation of plane (about table origin) by a vector
t * p transformation of a plane by a trans
.MAYBREAK
trans expressions:
f → f transformation which leads from the first frame to
the second
[v | v] composing a translation, then a rotation to make a
trans. Even though the translation is
written first, it is applied after the
rotation.
t + v translating a trans; modifies only the translation
part.
t ∂ v rotating a trans; modifies only the rotation part.
t * t composing two transes. Transes operate on the left.
.MAYBREAK
PREDEFINED CONSTANTS AND VARIABLES:
π is 3.14159...
TABLE is a frame which has standard table coordinates. (constant)
BLUE is the location of the blue hand.
YELLOW is the location of the yellow hand.
BPARK is where the blue hand parks. (constant)
YPARK is where the yellow hand parks. (constant)
X is (1,0,0).
Y is (0,1,0).
Z is (0,0,1).
.END
%*
Any expression preceeded with the symbol "α#" means "the planning value
of this expression", that is, a constant is substituted for the entire
expression in the expander.
.UNFILL
EXTRACTION FUNCTIONS:
LOC(FRAME) is a vector whose value is the location of the frame.
ORIENT(FRAME) is a vector whose value is the orientation of the frame.
XSCAL(VECTOR) is the X coordinate of the vector.
YSCAL(VECTOR) is the Y coordinate of the vector.
ZSCAL(VECTOR) is the Z coordinate of the vector.
NORMAL(PLANE) is the outward facing normal vector of a plane.
.REFILL
.NEWSSS SOME EXAMPLES OF ARITHMETIC EXPRESSIONS
.UNFILL
In the following examples, assume these declarations:
FRAME F1, F2, etc;
VECTOR V1, V2, etc;
SCALAR S1, S2, etc;
PLANE P1, P2, etc;
.MAYBREAK
F1'S unit Y vector, in TABLE coordinates:
F1*(0,1,0)
F1 * Y
.MAYBREAK
F1's Z vector as seen from F2:
(F2α→F1) * (0,0,1)
(F2α→F1) * Z
.MAYBREAK
A vector pointing in same direction as F1's X coordinate:
X WRT F1
.MAYBREAK
V1 rotated 90 degrees about the table's Z axis:
UNITS EULER;
V1 α∂ (90,0,0)
.MAYBREAK
F1's Y-Z plane:
LOC(F1) α\ (X WRT F1)
.MAYBREAK
A plane 3 units above the table:
(0,0,3) α\ Z
(3*Z) α\ Z
.REFILL
.NEWSS MOTIONS
.NEWSSS COMPILE-TIME AND RUNTIME CONSIDERATIONS
All motion statements cause the compiler to make some plans
for the eventual execution of the motion. These plans are more or
less complicated, depending on the exact type of motion requested.
Those motions which depend on the value of some frames as parameters
to the action will be planned using the compile-time planning values
for all relevant frames.
Immediately before the arm starts moving on a trajectory, the
plan is modified to bring it into line with current values of frames.
The result of this last-minute modification is that if there is any
discrepancy between the runtime and compile-time understanding of
where any frame is, the servo will place the arm in the right place
nonetheless. There are limits to the proper use of this feature; if
the planning value is seriously in error (and this can mean anywhere
from a few inches to a foot, depending on the arm being used and its
configuration), then the attempt to make last-minute corrections will
either overstrain the arm or impair force and free sensing (discussed
below).
It is the user's responsibility to foresee such
discrepancies in the planning value and to branch her program so that
several moves are planned (with ASSERT statements to inform the
compiler of the assumptions being used). The IF-THEN-ELSE statement
will be useful in performing the correct branch at runtime. Its
condition will most likely involve the location of a frame.
Actually, the compiler will eventually be able to do some of this
artificial splitting by using locus information and given tolerances.
The last step of any motion is the reevaluation of the
location of the hand, and the updating, if necessary, of the values
of all frames attached to it.
.NEWSSS SIMPLE MOVES
A move is simple if it involves only one arm.
A smooth
trajectory is compiled by
splining together polynomial segments (usually third degree,
occasionally fourth) separately for each arm joint. This trajectory
calculation is somewhat time-consuming, and is done completely at
compile time.
The arm is expected to travel from its current position
to the final position, passing through any specified intermediate
positions. The standard way to avoid the table is to begin motion
directly away from it and to end motion directly toward it. There is
a default offset associated with each frame, called its "deproach",
which is used to calculate the first and the last of the
intermediate, or "via" points. The deproach of a frame is stored in
the assertional data base, which is discussed in {sssref asr}.
An example:
.UNFILL
MOVE YELLOW α{Smooth motion, for yellow armα}
TO FROBGRASP α{Name of (frame) destinationα}
VIA SWING1, SWING2 α{Two via-pointsα}
.REFILL
This example demonstrates the general syntax; the reserved
word MOVE is followed by the name of the frame to be moved (usually an
arm) and a set of clauses, each beginning with a
reserved word (here the words TO and VIA). There is no punctuation
necessary at the end of a clause.
In the example above,
The first implicit via point will be the deproach point for whatever
frame at which the yellow arm is currently positioned. The last
implicit via point will be the deproach point for frobgrasp.
At each of the via-points, several conditions may be
specified. These are velocity and upper or lower bounds on the time
required to reach this frame from the previous one on the list. Also,
one may specify that a piece of code is to be initiated when a VIA
point is achieved; this is done with a THEN construct. The statement
following THEN may not be a jump or a motion statement for the same
arm. (If the statement is a motion statement, it must be surrounded
by BEGIN and END.)
An example:
.UNFILL
VIA F1 (VEL=3*Z), F2 THEN ENABLE "CH1", F3 (VEL=0, DURATION=5) .
.REFILL
This specifies three via points. At the first, the velocity is to be
3*Z, at the second a scalar variable is to be set, and at the third
the velocity is to be 0 and it should take 5 seconds to get there
from F2.
Certain things must be specified for any move. First is the
arm which is to be moved. It can be named directly (as YELLOW or
BLUE) or by naming a frame which is to be moved: If FROB is attached
to a hand, it is perfectly reasonable to request that the frob be
moved to a particular location. So if FROB is a frame attached to
BLUE, then both FROB and BLUE are "controllable frames"; MOVE FROB is
perfectly legal. A discussion of frame attachment can be found in
{ssref att}.
Next, the destination frame must be specified. TO F1 means
that at the end of the motion, the controllable frame (assume it is
an arm) should coincide with the frame F1. MOVE FROB TO F2 means
that at the end of the motion, FROB coincides with F2. A notational
convenience about destinations: They can be specified in terms of
where the controllable frame is at the start of the motion. The
symbol for this is ⊗. That is, ⊗ is a frame which is the location
and orientation of the controllable frame at the start of the motion.
Thus, ⊗ + (0,0,1) is a frame 1 unit above the starting place (in
table coordinates).
.NEWSSS OPTIONS FOR MOVES
DIRECTLY tells the compiler that only the via points and the
final point are of interest; no smooth trajectory need be planned.
A smooth motion will result due to runtime calculations.
This will also set the deproaches to NIL.
(Deproaches are discussed in {sssref dep}.)
ON requests that certain conditions be continually monitored
during motion. These can be conditions of any sort, including flag
checking, force checking, and time checking. If any monitored
condition triggers, the DO part associated with it will be executed.
The "block" of a motion-based ON monitor is the motion statement
itself; exiting the motion will disable the test.
Several functions can be tested continually; these include
force along a vector (FORCE(V)), time since beginning
of motion
(DURATION), and
the force between the fingers (SQUEEZE).
One more "function" is testable: ARRIVAL. This becomes true
when the motion terminates due to either having reached its
destination.
It does not become true if the arm stops for reasons other than normal
arrival at the destination; STOP does not trigger it.
An example:
.UNFILL
ON FORCE(Z)>10 DO
BEGIN WRITE("OUCH"); STOP END
.REFILL
TRACING is another option. It allows the user greater
control over the exact trajectory chosen for the move. The path can
be traced at whatever speed desired. The path, or "parameterized
frame", is a specification of what frame the arm is to go through
for each value of the parameter. Of course, one also specifies the
relation between the parameter and real time. It is also possible to
state the grain of the motion and the tolerance that is acceptable
(as a distance in 3-space).
An example:
.UNFILL
MOVE YELLOW
TRACING CENTER + (COS(P),SIN(P),0)
FOR P α← 0 UNTIL 2*π
WITHIN .1;
.REFILL
should move the yellow arm in a circle around CENTER.
The option MAINTAINING ORIENTATION causes
the trajectory computed by HAL to try to maintain the same
hand orientation throughout the motion. Of course, the final
orientation must be the same as the initial orientation for this to
work at all.
USING lists a set of modes under which the motion is to be
performed. These can include duration control, force applied in
some direction, which directions should be free from position
feedback, and what the departure and approach should be (if it is
desired to override the defaults, which are properties of the frames
involved at the beginning and end of motion).
Duration refers to the time elapsed since the start of
motion. In an ON-monitor, one can check for duration becoming too
long. In the USING construct, duration is merely a note for how long
the trajectory should be planned to take. One can use %7≥%*,=,or ≤ for
this, although ≤ is most likely risky.
Example:
.UNFILL
USING DURATION %7≥%* 3 .
.REFILL
Force specifies both a direction and an intensity. During
the motion, an attempt will be made to apply the required force.
This is done by applying certain forces in some combination of arm
joints. Which arm joints are affected is decided by the compiler; if
the motion is long, it is likely that the particular joints applying
force will be scheduled to change during the motion, as the aspect of
the arm changes. To get a force in the hand's Z direction, say, one
would write
.UNFILL
FORCE = Z WRT @ ,
.REFILL
where @ is a symbol meaning "the location
of the controllable frame, as continuously changing during motion."
A free direction is one in which all position errors are
ignored by the servo. As for forces, the compiler translates this
into a set of joints which are to have the position feedback
disabled, and this set may vary throughout the motion. Once again,
the @ may be used to refer to the controllable frame as it moves.
It is possible to free more than one direction, or apply
force in more than one direction. In this case, the directions
specified for force must be orthogonal, as must the directions
specified for free. This restriction is enforced by the requirement
that multiple forces and frees all be set with respect to the
cardinal directions of one frame. Examples:
.UNFILL
USING FORCE = Z WRT FROB, FORCE=X WRT FROB, FREE=Y WRT HAND
USING FORCE = (2,1,0), APPROACH = APPROACH(FROB);
COMMENT: This form will be explained in {sssref dep};
USING FREE = X, FREE = Y, DEPARTURE = NIL
.REFILL
Since both force and free are translated by the compiler into
special handling of certain joints, surprises can result from large
discrepancies between the planning values and the actual runtime
values. The motions will go through the right places, but the
directions of force and freedom may be wildly wrong.
The notions of force and free are hardware-dependent; they
depend on the particular arm in use. Hopefully, as more
sophisticated arms become available, USING can be extended to handle
whatever new capabilities exist.
.dep: NEWSSS DEPROACHES
Many objects have shapes which necessitate care as the arm
approaches them or departs from them. HAL supplies a method for
insuring that every time the arm approaches a frame, it will pass
through some associated spot first, and every time it leaves that
frame, it passes once again through the same spot. The "spot" is
termed a DEPROACH (from DEParture and apPROACH), and is really a
transformation to be applied to the frame involved in order to
discover the appropriate place through which to pass. The reason that
a transformation is used, and not a frame itself, is that deproaches
are often used for large objects, like the table, and the proper
point to pass through in that case is 10 centimeters above where the
hand is meant to arrive on the table (or above where the hand
currently is on the table, if a departure is meant), not the point
10 centimeters above the table origin.
One specifies the deproach of an object by making an
assertion; for example, the deproach of the table is automatically
asserted as follows (see {sssref asr} concerning assertions):
.UNFILL
ASSERT FACT (DEPROACH TABLE [(0,0,10) | (0,0,0)]) .
.REFILL
This means
that the correct departure point from a spot S on the table will be
.UNFILL
[(0,0,10) | (0,0,0)] * S .
.REFILL
As an object moves about in space, its deproach point moves
as well. Thus
.UNFILL
ASSERT FACT (DEPROACH FROB [(0,10,0) | (0,0,0)])
.REFILL
means that no matter where the frob may go, its deproach will be 10
centimeters in the y-direction away from its origin, as measured in
its own coordinate system.
What if the hand is not at FROB, but wishes to use its
deproach? This also is handled correctly; the deproach point will be
10 centimeters in FROB's y-direction from wherever the hand actually
is.
Deproaches, being transformations, also have the power to
include rotations. These are considered to be rotations about the
origin of the coordinate system involved; the rotation occurs, as
usual, before translation. Thus
.UNFILL
ASSERT FACT (DEPROACH FROB [(5,0,0) | (0,0,90)])
.REFILL
has the effect that whenever FROB's deproach is used from any frame, the
deproach frame will be the given frame, rotated about FROB's origin
by 90 degrees in Z, and then translated 5 centimeters in FROB's X
direction.
Suppose that a frame
F is given deproach transformation D. It is desired to find the
frame which is the deproach point from some other frame H (for
example, where the hand is, for departure), using F's deproach. The
frame which is used is
.UNFILL
F * D * (Fα→TABLE) * H .
.REFILL
If H = F, then the identities
.UNFILL
(F α→ TABLE) * F = TABLE, and X * TABLE =X .
.REFILL
reduce the expression for F's deproach to F * D.
When an arm moves to a frame, this is how the deproach point
is calculated: The frames own deproach is used, if it has one. If
not, then a search is made up the ladder of attachment (that is, frames
to which the given frame is attached are searched) until one is found
with a deproach.
If none at all is found, then the table's
deproach is used as a default. (One way to think of this is to
consider all frames ultimately attached to the table.) In approaching
a frame which is the result of a calculation (such as MOVE
YELLOW TO FROB + (0,0,1)) the default deproach is null.
The default deproach of ⊗ is also null.
In departing from a frame, it matters whether or not that frame is
now attached to the hand. If not, then the same algorithm for
finding deproach is used as in the case of approach. But if the frame
has been detached from some erstwhile mother and is now attached to
the hand, then its old mother's deproach is used (and if there is
none, the same search is made).
A frame
attached to the hand still has some "memory" of its previous state of
attachment, by means of an automatic WAS_ATTACHED assertion
which will be mentioned in {ssref att}.
All of the automatic generation of deproach points can be
explicitly overridden in a MOVE statement by means of the USING
clause. This can take two forms:
.UNFILL
USING APPROACH = NIL; COMMENT: No approach point at all is used;
USING APPROACH = DEPROACH(frob); COMMENT: frob's deproach is used;
.REFILL
Note that the word APPROACH could be replaced by DEPROACH in
both of the examples above.
.NEWSSS COMPLEX MOVES
A complex move is one which involves more than one arm at a
time. A distinction can be made between moves which merely require
simultaneous acquisition of "agreement points" (let us call this weak
synchrony), and those which require true coordinated motion
throughout (strong synchrony).
Weak synchrony is achieved by pairing frames to make composite
VIA points and destinations. A paired frame has the form: α{F1, F2α}.
Here is an example of a move statement using paired frames:
.UNFILL
MOVE α{YELLOW, BLUEα}
VIA α{Y1,B1α},α{Y2,*α},α{Y3,B2α}
TO α{Y4,B3α}
ON α{FORCE(Z)>3,*α} DO STOP
.REFILL
The via list is composed of a set of paired frames, where *
indicates "don't care". In the example shown, the arms start
together, achieve Y1 and B1 simultaneously, the yellow arm passes
through Y2, and together they pass through Y3 and B2.
It is now more cumbersome to specify ON monitors, or
conditions in general. The paired construct applies for all the
optional fields; thus, one can write
.UNFILL
USING FORCE=α{3*Z,(2*Y) WRT @α}
.REFILL
to get the yellow arm applying a
force of strength 3*Z, and the blue one to have a force of strength 2*Y
in the coordinate system of the blue hand.
The meaning of ⊗ and @ is now relative to which side of the
pair they occupy; in the example above, the left side always refers
to the yellow arm, and the right side to the blue. To override this
convention, one may use expressions like "@.YELLOW", or "⊗.BLUE".
The meaning of STOP in the example above is extended to both
arms at once; in order to specify only one, it is necessary to say
"STOP YELLOW" or "STOP BLUE".
Strong synchrony involves one concept not included above: The
ability to specify the location of one arm throughout the motion in
terms of the location of the other arm. The construct which allows
this specification is COORDINATING; it allows one to give an
expression for the location of one of the two arms. Suppose we wish
to keep both arms in "lockstep", that is, the blue arm should retain
its relative position to the yellow arm throughout the motion. (This
might be necessary for lifting some object by its two ends.) One way
to code this task is as follows:
.UNFILL
MOVE α{YELLOW, BLUEα}
TO α{Y1, *α}
VIA α{YA,BAα},α{YB,BBα}
COORDINATING LOC.BLUE = LOC.YELLOW + ⊗.BLUE - ⊗.YELLOW
USING FREE = α{*, @.YELLOW - @.BLUEα}
α{MAINTAINING ORIENT, MAINTAINING ORIENTα}
.REFILL
.NEWSSS SEARCHES
A SEARCH is very much like a move. It is a means of
specifying repeated action in a spiral. As with a MOVE, it is
necessary to name a controllable frame which is to be moved. The ON
construct is exactly as for MOVEs.
One must stipulate what the plane of the search is to be.
This is accomplished in either of two ways: ACROSS <plane> means the
search is to take place in the plane specified. If the controllable
frame (say, the hand) is in fact not in that plane at the start, then
the plane parallel to the given one through the hand will be used. It
is assumed that the hand begins at the center of the search. The
other alternative is to say NORMAL_TO <vector>. This,
together with the location of the hand,
will specify
the plane you want for the search.
The size of the increment is specified in a USING construct.
An example is USING INCREMENT = 3.
The servo does almost all the calculating for searches; it is
fed the normal direction and the increment size.
Most important for the search is the REPEATING construct, which
specifies what motion is to be performed at each
iteration. It is advisable that the motion cause the
arm to return to the point at which it began, in order to assume the
same plane at the onset of each iteration. If this is not done, then
the servo will move it back each time anyway. When the search
succeeds (and it is the duty of the user to specify what success
means for each search) the search can be terminated in either of two
ways: by setting a flag in the REPEATING and checking it with an ON,
or by using the construct TERMINATE inside the REPEATING at the right
place.
Here is a complete example:
.UNFILL
SEARCH YELLOW
ACROSS P1
REPEATING
BEGIN
FRAME SET;
SET α← YELLOW;
MOVE YELLOW TO ⊗-Z
ON FORCE(Z) > 3 DO TERMINATE;
MOVE YELLOW TO SET DIRECTLY;
END
.REFILL
.NEWSSS CENTER
Occasionally the hand is positioned around an object, but it
is not certain if it is centered. One wants to close the fingers
slowly, moving the arm meanwhile to accomodate to the location of
the object.
This is accomplished by means of the CENTER command.
The direction that the hand will move is the direction between its
fingers. All that the CENTER command needs is the name of the
arm being moved.
The use of ON is just as in a
search or any other motion.
Here is a simple example:
.UNFILL
CENTER BLUE
ON SQUEEZE > 4 DO STOP
.REFILL
Note that this is command, unlike MOVE, treats the fingers and
the arm together as one device.
.NEWSSS CONSTANT VELOCITY MOTION
A special form of the MOVE instruction is provided
to cause the arm to quickly achieve a particular velocity,
and to hold it in straight-line motion for a given distance:
.UNFILL
MOVE YELLOW
VELOCITY=V1
THROUGH T
FOR DISTANCE=4 .
.REFILL
The VELOCITY clause tells which vector to follow, and how fast. The
THROUGH clause tells the compiler where the move expects to end. The FOR
DISTANCE tells the maximum distance the hand should go. It is assumed
that such a move will normally terminate by an ON test.
For example:
.UNFILL
MOVE YELLOW
WITH VELOCITY=V1
THROUGH T
FOR DISTANCE=4
ON FORCE(V1) > 2 DO STOP
.REFILL
.NEWSSS DEVICE CONTROL
Generally, an arm will stop its motion when it has achieved
its destination. Often it is necessary to stop it prematurely, for
example, if some error condition is detected. The statement STOP
YELLOW causes the yellow arm to be unconditionally stopped, and any
motion statement operating it will terminate. Each device has a name,
and can be stopped by name. Currently, the legal device names are
YELLOW, BLUE, VICE, DRIVER (an electric screwdriver), YFINGERS,
BFINGERS (The fingers of the two arms). STOP without any device name
is only legal within a motion command; it stops whatever device(s) that
command is operating.
There is a general command for operating devices other than
arms; it is hoped that this will be flexible enough for any device we
are likely to use (if not, we will add special new forms). Assume we
have the device TURNTABLE, which is capable of moving at any velocity
and for any length of time, but which cannot go to a particular set
point. Then the syntax would be this:
.UNFILL
OPERATE TURNTABLE
WITH VELOCITY=3
WITH DURATION=8
.REFILL
The idea is that the WITH construct will suffice to account for any
special data (in this case, velocity and duration) peculiar to the
particular device. The OPERATE statement also allows the ON
construct, so it can test for special conditions and take appropriate
actions.
The screwdriver is a hand-held device which can be run at a
range of speeds, in either direction. By convention, a positive
velocity means clockwise, and a negative velocity means
anticlockwise. The relevant reserved word is VELOCITY, which is
equated with the name of a scalar variable, which will be queried
each time the screwdriver servo wakes up to determine how much
voltage to apply to the motor. This allows the velocity to change
during the operation of the device, perhaps under the control of a
parallel process which is monitoring some conditions.
An example:
.UNFILL
OPERATE DRIVER
WITH VELOCITY=SP
ON DURATION>4 DO STOP
ON DURATION>2 DO SP α← 2*SP
.REFILL
Each arm has two fingers at the end which are capable of
closing and opening at various speeds. The relevant reserved words
are OPENING, which is to be set to the desired (scalar) opening, and
VELOCITY, which is to be set to the speed desired. It is possible to
refer to the scalar variable SQUEEZE, which indicates the force being
applied by the fingers.
An example:
.UNFILL
OPERATE FINGERS
WITH OPENING=4
WITH VELOCITY=2
ON SQUEEZE > 3 DO STOP
.REFILL
.att: NEWSS ATTACHMENT
Assembly often involves affixing one object to another. HAL
has a mechanism to automatically keep track of the location of a
subsidiary piece of the assembly as its base is moved; the mechanism
is called attachment. For example, there might be a frame called
PUMP and a frame called BASE. At some stage in the assembly, the
pump is bolted to the base. At this time it is appropriate to
include the statement
.UNFILL
ATTACH PUMP TO BASE
.REFILL
The effect of this is severalfold: It informs the compiler that
motions of BASE are to affect the location of PUMP, it generates the
assertion
.UNFILL
ASSERT FACT (ATTACHED PUMP BASE)
.REFILL
and it causes code to be generated for the runtime which will
automatically update the value of PUMP every time BASE is changed.
Please note that the ATTACH statement does not act as a library
routine invocation; it does not generate code to actually perform the
bolting operation. The statement merely informs the HAL system that
at this stage in the execution of the program, PUMP is to be
considered attached to BASE.
If PUMP should be moved while attached to BASE, the value of
BASE itself will not change, but the attachment will remain for the
new relative positions of PUMP and BASE. Occasionally it is desired
that the attachment be symmetric, so that motion of either frame will
cause the other to move. This is done by including the reserved word
RIGIDLY in the attach statement:
.UNFILL
ATTACH PUMP TO BASE RIGIDLY
.REFILL
A more precise definition of attachment in terms of graph structures
is given in {sssref gat}. Here, we should point out that the
system uses a trans to store the relative positions
(in our example, "(BASE α→ PUMP)" ) of the
attached frames. Normally, the system would invent a
temporary variable to hold this trans; however, the user can
supply her own variable to be used instead, thus allowing her
to modify directly the attachment relation. This is done by including
the phrase "BY <transform variable id>" in the ATTACH statement.
For instance,
.UNFILL
ATTACH PUMP TO BASE BY T1;
.REFILL
If the value of the trans is modified in a non-rigid (that
is, assymetric) attachment, the effect is to move the subsidiary
frame. If the value of the trans changes in a rigid
(symmetric) attachment, then neither frame will change its value until
one of them explicitly gets a new value; at that time the other will spring
to a new position, as determined by the trans.
The inclusion of the construct "AT <transform expression>" will cause
HAL to use the indicated value for the relative attached position of the
objects. Thus,
.UNFILL
ATTACH PUMP TO BASE AT [(0,0,0)|(0,0,0)]
.bull
is equivalent to
ATTACH PUMP TO BASE BY TEMPXF;
TEMPXF α← [(0,0,0)|(0,0,0)]
.REFILL
It is possible to make chains of attachments, possibly
involving some rigid attachments and some non-rigid ones.
Attachments are undone by the DETACH statement. For example,
.UNFILL
DETACH PUMP FROM BASE
.REFILL
will remove the attach structure between PUMP and BASE, and will discard
the invented trans (unless it was named, of course). Two changes in
the compiler's assertional data base will also be generated
(See the section on assertions):
.unfill
DENY FACT (ATTACHED PUMP BASE);
ASSERT FACT (WAS_ATTACHED PUMP BASE)
.REFILL
The latter assertion
is used in calculation of default deproach points. A side-effect of
any assignment, like F1 α← <value>, is
.UNFILL
DENY FACT (WAS_ATTACHED ANYTHING F1);
DENY FACT (WAS_ATTACHED F1 ANYTHING) .
.REFILL
.gph:NEWSS GRAPH STRUCTURES
Attachments are stored in both the compiler and the runtime as a
graph structure. The nature of this structure is described
in {sssref rgf};
the algorithms which serve to extract values from a graph and
insert new values into it
can be found in Appendix II.
Suffice it here to say that if the value
of a variable is needed, and that value is marked as invalid, then
the list of calculator expressions for that variable is searched for
one which can compute a valid value; if none of the calculators will
work
(e.g. they all depend themselves on invalid values),
then the current (invalid) value is returned as the best answer
available. When a new value is assigned to a variable, all those
values whose calculators depend on the new value are marked as
invalid, so that the next time they are needed, graph searching is
performed. Building a graph structure therefore involves specifying
the calculators for all variables. Usually, this will be be done
implicitly by means of "intuitively obvious" statements like ATTACH
and DETACH. However, HAL also supplies primitives for explicit
manipulation of graph structure. The principal explicit means
employed for this purpose is the "graph assignment statement":
.UNFILL
<variable> <= <expression>
.REFILL
where "<=" may be read "is computed by". This construct causes
<expression> to be added to the list of calculators for <variable>.
Similarly,
.UNFILL
<variable> <%7≠%* <expression>
.REFILL
causes <expression> to be removed from the list of calculators, and
.UNFILL
<variable> <<= <expression>
.REFILL
replaces the current calculator list for <expression>. The statement
.UNFILL
<variable> <<= ;
.REFILL
would cause the calculator list to be set to null.
It is frequently very inconvenient to retype an entire expression in
order to remove it from the calculator list. HAL allows the user to
attach a name to an expression in a graph assignment statement by use
of the construct
.UNFILL
<variable> <= "id" <expression>
.REFILL
Then the construct
.UNFILL
<variable> <%7≠%* "id"
.REFILL
will remove the named expression from the calculator list. For
instance,
.UNFILL
F1 <= "foo" T*F2;
:
F1 <%7≠%* "foo";
.REFILL
would have the same effect as
.UNFILL
F1 <= T*F2;
:
F1 <%7≠%* T*F2;
.REFILL
In addition to the calculator list, a list of "updater" routines is
associated with every variable. These routines are executed whenever
the variable value is changed. Initially, the list of updaters is
null. However, the construct
.UNFILL
WHEN CHANGING <variable> ALSO DO "<id>" <statement>;
.REFILL
will cause <statement> to be added to the list of updaters for
<variable>. ("<id>" is optional, but is necessary if the <statement>
is ever to be removed from the updater list.) In <statement>, the
reserved words OLD and NEW may be used to refer to the old and new
values of var, respectively. For instance:
.UNFILL
WHEN CHANGING F2 ALSO DO "foo" F1α←NEW*(OLDα→F1);
.REFILL
Updaters may be removed from the updater list by the statement
.UNFILL
WHEN CHANGING <variable> DONT DO "<id>"
.REFILL
For our above example, this would be
.UNFILL
WHEN CHANGING F2 DONT DO "foo";
.REFILL
The form
.UNFILL
WHEN CHANGING <variable> ONLY DO <statement>;
.REFILL
replaces the updater list with one containing just <statement>, and
.UNFILL
WHEN CHANGING <variable> ONLY DO ;
.REFILL
clears the updater list completely. Since the attach structure makes
use of updater and calculator lists (see {sssref rgf}), careless use of the
replacement form is not advised.
One good use for updater routines is tracing. For example,
.UNFILL
WHEN CHANGING V ALSO DO
WRITE("The value of V is now ",NEW);
.REFILL
Details of the graph structure algorithms may be found in the appendix
on runtime routines.
One additional point that should be mentioned here is that the
updater routines for a variable are NOT called if the variable's
value is modified as a side effect of a change to some variable in
one of its calculators.
.gat: NEWSSS CONCERNING ATTACH AND DETACH
The ATTACH and DETACH statements are defined by their effects on the
graph structures.
.UNFILL
ATTACH F1 TO F2 BY T1
.UNFILL
is equivalent to
.REFILL
T1 α← F2α→F1;
F1 <= "xxx" T1 * F2;
WHEN CHANGING F1 ALSO DO "yyy" T1α←(F2α→NEW);
ASSERT FACT (ATTACHED F1 F2 T1);
Comment : For details of ASSERT see {sssref asr};
.REFILL
Then,
.UNFILL
DETACH F1 FROM F2;
.REFILL
is equivalent to
.UNFILL
F1 <%7≠%* "xxx";
WHEN CHANGING F1 DONT DO "yyy";
DENY FACT(ATTACHED F1 F2 ANYTHING);
ASSERT FACT (WAS_ATTACHED F1 F2);
.REFILL
Similarly,
.UNFILL
ATTACH F1 TO F2 BY T1 RIGIDLY
.REFILL
is equivalent to
.UNFILL
T1 α← F2α→F1;
F1 <= T1 * F2;
F2 <= INVERSE(T1) * F1 .
.REFILL